package video;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.UUID;

/**
 * aes加解密工具
 * https://blog.csdn.net/qq_43369406/article/details/123346003
 * AES属于分组加密算法，属于对称加密，故在传递密钥途中有密钥泄露的风险
 * https://blog.csdn.net/zhanglf02/article/details/100124091
 */
public class AesUtil {
    private final static String AES = "AES";
    private final static String UTF8 = "UTF-8";
    // 定义一个16byte的初始向量，由于m3u8没有IV值，则设置为0即可
    private static final String IV_STRING = "0000000000000000";

    /**
     * 产生一个16位的密钥字符串
     *
     * @return
     */
    public static String generateSecreKey() {
        String uuid = UUID.randomUUID().toString();
        uuid = uuid.replaceAll("-", "");
        return uuid.substring(0, 16);
    }

    /**
     * 加密
     */
    public static String aesEncry(String content, String key) throws UnsupportedEncodingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException {
        byte[] contentByte = content.getBytes(UTF8);
        byte[] keyByte = key.getBytes();
        // 初始化一个密钥对象
        SecretKeySpec keySpec = new SecretKeySpec(keyByte, AES);
        // 初始化一个初始向量,不传入的话，则默认用全0的初始向量
        byte[] initParam = IV_STRING.getBytes();
        IvParameterSpec ivSpec = new IvParameterSpec(initParam);
        // 指定加密的算法、工作模式和填充方式
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
        byte[] encryptedBytes = cipher.doFinal(contentByte);
        String encodedString = Base64.getEncoder().encodeToString(encryptedBytes);
        return encodedString;
    }

    /**
     * 解密ts
     * https://www.freesion.com/article/64051025850/
     * JAVA下载M3U8视频，解密并合并TS（三）
     * https://github.com/qq494257084/m3u8Download
     * https://blog.csdn.net/qq494257084/article/details/103551293
     */
    public static byte[] aesDecry(byte[] contentByte, String key) throws Exception {
        // byte[] contentByte = Base64.getDecoder().decode(content);
        byte[] keyByte = key.getBytes();
        // 初始化一个密钥对象
        SecretKeySpec keySpec = new SecretKeySpec(keyByte, AES);
        // 初始化一个初始向量,不传入的话，则默认用全0的初始向量
        byte[] initParam = IV_STRING.getBytes();
        IvParameterSpec ivSpec = new IvParameterSpec(initParam);
        // 2种方式其实是一样的 byte默认是0
        // IvParameterSpec ivSpec = new IvParameterSpec(new byte[16]);

        // pkcs5 padding和pkcs7 padding的区别
        // https://blog.csdn.net/boweiqiang/article/details/109860950
        // 如果需要加密的数据（明文）的字节码的长度不是块大小的整数倍，那么就需要在末尾进行填充。那么如何填充呢？这就是本文要说的pkcs5和pkcs7填充模式
        // 最后，说一下PKCS7和PKCS5的区别就是数据分块的大小（就是这么简单）：
        //  PKCS5填充块的大小为8bytes(64位)
        //  PKCS7填充块的大小可以在1-255bytes之间。
        // 综上所述，可以说PKCS7是兼容PKCS5的，PKCS5相当于PKCS7的一个子集。
        // 因为AES的要求的分块长度固定为128 比特（密钥key长度则可以是128，192或256比特），所以填充方法一定是PKCS7

        // 块大小是使用的密码算法的属性。对于AES，它总是16个字节。
        // 严格来说，PKCS5Padding不能与AES一起使用，因为它仅定义为8字节的块大小。
        // 我假设，AES / CBC / PKCS5Padding在内部被解释为AES / CBC / PKCS7Padding。
        // 这些填充方案之间的唯一区别是PKCS7Padding具有块大小作为参数，
        // 而对于PKCS5Padding，它固定为8个字节。当块大小为8字节时，它们完全相同。
        // 指定加密的算法、工作模式和填充方式
        // 指定加密的算法、工作模式和填充方式
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        // Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");

        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
        byte[] result = cipher.doFinal(contentByte);

        return result;
    }

}

